package com.econ.powercloud.ems.service.biz;

import com.econ.powercloud.clickhouse.entity.EmsStatisticalMonth;
import com.econ.powercloud.common.base.facade.enums.StatusType;
import com.econ.powercloud.common.base.facade.exception.CommonRuntimeException;
import com.econ.powercloud.common.base.facade.util.JacksonUtil;
import com.econ.powercloud.ems.common.enums.FeeCalType;
import com.econ.powercloud.ems.common.enums.FeeConfirmReportLocationDesc;
import com.econ.powercloud.ems.common.enums.ReportIdType;
import com.econ.powercloud.ems.common.util.KeepDecimalPointUtil;
import com.econ.powercloud.ems.config.EMSStaticMap;
import com.econ.powercloud.ems.constant.SysConstant;
import com.econ.powercloud.ems.dal.entity.EmsEnergyStation;
import com.econ.powercloud.ems.dal.entity.EmsFeeConfirmConfig;
import com.econ.powercloud.ems.dal.entity.EmsFeeConfirmExplain;
import com.econ.powercloud.ems.domain.EmsFeeConfirmConfigDO;
import com.econ.powercloud.ems.pojo.EmsFeeConfirmConfigVO;
import com.econ.powercloud.ems.pojo.requestVO.ReportRequestVO;
import com.econ.powercloud.ems.service.dao.EmsEnergyStationDAO;
import com.econ.powercloud.ems.service.dao.EmsFeeConfirmConfigDAO;
import com.econ.powercloud.ems.service.dao.EmsFeeConfirmExplainDAO;
import com.econ.powercloud.ems.util.TimeUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author : zjf
 * @Description :
 * @Date : 2022/4/24 10:47
 * @Modified by : zjf
 */
@Service
public class FeeConfirmService {

    @Autowired
    private EmsFeeConfirmConfigDAO emsFeeConfirmConfigDAO;
    @Autowired
    private EmsFeeConfirmExplainDAO emsFeeConfirmExplainDAO;
    @Autowired
    private EmsEnergyStationDAO emsEnergyStationDAO;
    @Autowired
    private ClickHouseBizService clickHouseBizService;

    /**
     * 获取储能装置电费结算确认表
     * @return
     */
    public List<Map<String,String>> getDataByCondition(String stationId,String equipmentId,Long startTime,Long endTime){
        List<Map<String,String>> result = Lists.newArrayList();
        //开始时间,结束时间改成那一天的开始时间
        startTime = TimeUtil.getDayStartTime(new Date(startTime));
        endTime = TimeUtil.getDayStartTime(new Date(endTime));
        //获取电费结算确认表的所有列的数据
        List<EmsFeeConfirmConfig> confirmConfigs = emsFeeConfirmConfigDAO.list();
        List<EmsFeeConfirmConfigVO> configVOS = EmsFeeConfirmConfigDO.entityListToVOList(confirmConfigs);
        //按照行进行分组
        Map<Integer,List<EmsFeeConfirmConfigVO>> configMap = configVOS.stream().collect(Collectors.groupingBy(d->d.getCross()));
        //获取开始时间至结束时间的数据
        List<EmsStatisticalMonth> statisticalMonthList = clickHouseBizService.getMonthDataByCondition(
                ReportRequestVO.builder().stationId(stationId).equipmentId(equipmentId).reportId(ReportIdType.INCOME.getCode())
                        .startTime(startTime).endTime(endTime).build());
        Map<String,List<EmsStatisticalMonth>> statisticalMonthMap = statisticalMonthList.stream().sorted(Comparator.comparing(EmsStatisticalMonth::getTime))
                .collect(Collectors.groupingBy(d->d.getColumnId(), LinkedHashMap::new,Collectors.toList()));
        //判断开始时间与结束时间是否存在数据，不存在数据抛出异常(这里采用分享比例这个字段来判断)
        List<EmsStatisticalMonth> judgeList = statisticalMonthMap.get("shareRatio");
        this.judgeData(judgeList,startTime,endTime);
        //获取表头(目前是存在EMSStaticMap中,项目初始化时加载)
        List<String> incomeDayReportHead = EMSStaticMap.feeConfirmReportHead;
        for (int i=1; i<=configMap.keySet().size(); i++){
            Map<String,String> map = Maps.newLinkedHashMap();
            List<EmsFeeConfirmConfigVO> list = CollectionUtils.isEmpty(configMap.get(i))? Lists.newArrayList():configMap.get(i);
            //key:列的位置
            Map<Integer, EmsFeeConfirmConfigVO> deviceVOMap = list.stream().collect(Collectors.toMap(EmsFeeConfirmConfigVO::getGroup, p->p,(p1, p2)->p1));
            //在按照类别,项目,本期示数等表头分装数据
            for (int j=1; j<=incomeDayReportHead.size(); j++){
                if(deviceVOMap.get(j) != null){
                    List<EmsStatisticalMonth> monthList = statisticalMonthMap.get(deviceVOMap.get(j).getColumnId());
                    map.put(incomeDayReportHead.get(j-1),this.getFeeConfirmColumnIdVal(monthList,deviceVOMap.get(j)));
                }else{
                    map.put(incomeDayReportHead.get(j-1), FeeConfirmReportLocationDesc.getDescFromCode(i + SysConstant.CABLE + j));
                }
            }
            result.add(map);
        }
        return result;
    }

    /**
     * 根据储能站获取特别说明文案
     */
    public String selectSpecialExplainByStationId(String stationId){
        EmsEnergyStation station = emsEnergyStationDAO.selectOneByStationId(stationId);
        EmsFeeConfirmExplain confirmExplain = emsFeeConfirmExplainDAO.selectByProvinceAndCity(station.getProvince(),station.getCity());
        return confirmExplain == null?null:confirmExplain.getSpecialExplain();
    }

    /**
     * 判断电费结算确认的数据是否正确
     */
    private void judgeData(List<EmsStatisticalMonth> judgeList, Long startTime, Long endTime){
        if(CollectionUtils.isEmpty(judgeList)){
            throw new CommonRuntimeException(StatusType.FAIL,"当前选择的开始日期或结束日期没有数据，请重新选择!");
        }
        if(!startTime.equals(judgeList.get(0).getTime())){
            throw new CommonRuntimeException(StatusType.FAIL,"当前选择的开始日期或结束日期没有数据，请重新选择");
        }
        if(!endTime.equals(judgeList.get(judgeList.size()-1).getTime())){
            throw new CommonRuntimeException(StatusType.FAIL,"当前选择的开始日期或结束日期没有数据，请重新选择");
        }
    }

    /**
     * 获取读数,这里需要注意monthList已经是按照时间升序排列好的
     */
    private String getFeeConfirmColumnIdVal(List<EmsStatisticalMonth> monthList, EmsFeeConfirmConfigVO configVO){
        if(CollectionUtils.isEmpty(monthList)){
            return null;
        }
        FeeCalType feeCalType = FeeCalType.fromCode(configVO.getFeeCalType());
        switch (feeCalType){
            case MIN_TIME_VAL:
            case FIRST_TIME_VAL:
                return monthList.get(0).getVal();
            case MAX_TIME_VAL:
                return monthList.get(monthList.size()-1).getVal();
            case SUM_TIME_VAL:
                Double val = 0.0;
                for (EmsStatisticalMonth statisticalMonth : monthList){
                    val += Double.parseDouble(statisticalMonth.getVal());
                }
                return KeepDecimalPointUtil.keepTwoPoint(val);
            default:
                return null;
        }
    }
}
